#ifndef CNC_BASE_MEMORY_H
#define CNC_BASE_MEMORY_H

#include "register_.h"
#include <string>
#include <map>
#include <exception>
#include <bcm/bcm.h>
//provided interface
#include "globaldata.h"

namespace cnc {

	namespace base {

		class Memory : public bcm::Identifier<bcm::Component>,
			public bcm::Identifier<GlobalData>,
			public ChangeManager
		{
			typedef std::map<std::string, Register *> RegisterMap_;
			typedef std::multimap<std::string, Observer *> ObserverMap_;
			RegisterMap_ registerMap_;
			ObserverMap_ observerMap_;
		protected:
			Memory() : bcm::Identifier<bcm::Component>("mem", "Base Memory"),
				bcm::Identifier<GlobalData>("gdata", "Global Data")
			{
			}
			~Memory()
			{
				for (RegisterMap_::iterator it = registerMap_.begin();
					it != registerMap_.end(); ++it)
					delete reinterpret_cast<RegisterImpl *>((*it).second);
			}
		protected: // bcm::Component
			virtual bcm::IdentifierData *getProvidedInterfaceTable()
			{
				static bcm::IdentifierData idData[] = {
					{ "gdata", "Global Data" },
					{ 0, 0 }
				};
				return idData;
			}
			virtual bcm::IdentifierData *getRequiredInterfaceTable()
			{
				static bcm::IdentifierData idData[] = {
					{ 0, 0 }
				};
				return idData;
			}
			virtual bcm::Interface *getInterface(const char *id)
			{
				if (std::string("gdata") == id)
					return static_cast<GlobalData *>(this);
				return 0;
			}
			virtual bool setInterface(Interface *iface)
			{
				return false;
			}
		private: // ChangeManager
			void notifyObservers(std::string registerID)
			{
				std::pair<ObserverMap_::iterator, ObserverMap_::iterator>
				range = observerMap_.equal_range(registerID);
				for (ObserverMap_::iterator it = range.first;
					it != range.second; ++it)
					(*it).second->update(registerID.c_str());
			}
			virtual void notify(Register *subject)
			{
				for (RegisterMap_::iterator it = registerMap_.begin();
					it != registerMap_.end(); ++it)
					if ((*it).second == subject)
						notifyObservers((*it).first);
			}
		protected: // GlobalData
			virtual bool addRegister(const char *registerID, DataTypeID dataType)
			{
				RegisterImpl *reg;
				try {
					reg = new RegisterImpl(dataType);
					reg->setChangeManager(this);
				}
				catch (std::bad_alloc &) {
					return false;
				}
				registerMap_[registerID] = reg;
				return true;
			}
			virtual Register *getRegister(const char *registerID)
			{
				RegisterMap_::iterator it = registerMap_.find(registerID);
				if (it == registerMap_.end())
					return 0;
				return registerMap_[registerID];
			}
			virtual bool removeRegister(const char *registerID)
			{
				registerMap_.erase(registerID);
				observerMap_.erase(registerID);
				return true;
			}
			virtual bool addObserver(const char *registerID, Observer *observer)
			{
				observerMap_.insert(std::pair<std::string, Observer *>(registerID, observer));
				return true;
			}
			virtual bool removeObserver(const char *registerID, Observer *observer)
			{
				for (ObserverMap_::iterator it = observerMap_.begin();
					it != observerMap_.end(); ++it)
					if ((*it).first == registerID && (*it).second == observer) {
						observerMap_.erase(it);
						return true;
					}
				return false;
			}
		public:
			static Memory &instance()
			{
				static Memory object;
				return object;
			}
		};

	extern "C" Memory *getMemory();

	} // namespace base

} // namespace cnc

#endif
